using System;
using System.Collections;
using System.Threading;

namespace PaulRz.Util
{
    /// <summary>
    /// Implements a Session memory for caching objects which goes through MG
    /// Session is common for all templates defined in given windows service (Dispatcher or MG)
    /// </summary>
    public sealed class MemorySession : Session
    {
        private MemorySession()
        {
        }


        private static MemorySession instance = new MemorySession();

        public static MemorySession Instance
        {
            get { return instance; }
        }

        private Hashtable values = new Hashtable();

        /// <summary>
        /// Adds passed attributes to the session. If object with given is allready exists, the old value will be overwritten.
        /// </summary>
        /// <param name="id">messageId or other "key"</param>
        /// <param name="attributes">Hashtable of values</param>
        /// <param name="secondsToExpire">Number of secodns to invalidate given record</param>
        public override void Add(string id, Hashtable attributes, int secondsToExpire)
        {
            SessionItem sci = new SessionItem();
            if (attributes == null)
                attributes = new Hashtable();
            DateTime now = DateTime.Now;
            sci.Attributes = attributes;
            sci.SessionId = id;
            sci.Expiration = now + new TimeSpan(0, 0, 0, secondsToExpire, 0);
            sci.Connector = Connector;
            sci.TemplateName = TemplateName;
            lock (values.SyncRoot)
            {
                values[id] = sci;
            }
            report.Writes++;
        }


        /// <summary>
        /// Adds passed attributes to the session. If object with given is allready exists, the old value will be overwritten.
        /// </summary>
        public override void AddAttribute(string id, string key, string value, int secondsToExpire)
        {
            SessionItem si = null;
            lock (values.SyncRoot)
            {
                si = (SessionItem)values[id];
            }
            if (si == null)
            {
                Hashtable attributes = new Hashtable();
                attributes.Add(key, value);
                Add(id, attributes, secondsToExpire);
            }
            else
            {
                si.Attributes[key] = value;
            }
        }

        /// <summary>
        /// Returns specified message attribute from message with given id.
        /// If id or attribute name hasn't found the empy string would be returned
        /// </summary>
        /// <param name="id">messageId or other "key"</param>
        /// <param name="propertyName">attribute name</param>
        /// <returns></returns>
        public override string GetValue(string id, string propertyName)
        {
            string value = (string) this[id][propertyName];
            return value == null ? "" : value;
        }

        /// <summary>
        /// Returns a hashtable of all attributes from message with specified id
        /// </summary>
        public override Hashtable this[string id]
        {
            get
            {
                SessionItem item = null;
                lock (values.SyncRoot) 
                {
                    item = (SessionItem)values[id];
                }
                if (item != null)
                {
                    report.Found++;
                    return item.Attributes;
                }
                report.NotFound++;
                return new Hashtable();
            }
        }

        public override bool Contains(string id)
        {
            lock (values.SyncRoot) 
            {
                return values.ContainsKey(id);
            }
        }

        /// <summary>
        /// Cleans all expired ContextItems
        /// </summary>
        public override void Clean(object dummy)
        {
            int count = 0;
            try
            {
                ArrayList removeList = new ArrayList();

                DateTime currentDate = DateTime.Now;
                lock (values.SyncRoot)
                {
                    foreach (DictionaryEntry entry in values)
                    {
                        SessionItem sci = (SessionItem) entry.Value;
                        if (currentDate > sci.Expiration)
                        {
                            removeList.Add(entry.Key);
                            count++;
                            OnCleanWrapper(sci);
                        }
                    }
                    FUtil.removeFrom(values, removeList);
                }

                if (count > 0)
                    Logger.LogActivity("Memory Session Cleaner -  Removed " + count + " of " +
                                       (GetSessionCount() + count) + " messages.");
            }
            catch (Exception e)
            {
                Logger.LogError("An exception during MemorySession cleaner " + e.ToString());
                throw;
            }
        }


        private static Timer cleaner;

        public static void InitCleaner(int refreshPeriod)
        {
            if (cleaner == null)
                cleaner = new Timer(new TimerCallback(Instance.Clean), null, 0, refreshPeriod*1000);
        }

        /// <summary>
        /// Returns count of messages in session
        /// </summary>
        /// <returns></returns>
        public override int GetSessionCount()
        {
            lock (values.SyncRoot)
            {
                return values.Count;
            }
        }

        public override void Commit()
        {
            //no implementation for Memory  session
        }

        public override void Remove(string sessionId)
        {
            lock (values.SyncRoot) 
            {
                values.Remove(sessionId);
            }
        }

        public override void CleanAllSessions()
        {
            lock (values.SyncRoot) 
            {
                values.Clear();
            }
        }

        /// <summary>
        /// Returns array of all sessions, containing specified propertyName = propertyValue
        /// </summary>
        /// <param name="propertyName">attribute name</param>
        /// <param name="propertyValue">attribute value</param>
        /// <returns></returns>
        public override SessionItem[] GetSessionsContaining(string propertyName, string propertyValue)
        {
            ArrayList items = new ArrayList();
            lock (values.SyncRoot) 
            {
                foreach (DictionaryEntry session in values)  //full scan :(
                {
                    SessionItem item = (SessionItem)session.Value;
                    if (item != null)
                    {
                        if ((string)item.Attributes[propertyName] == propertyValue)
                            items.Add(item);
                    }
                }
            }
            return (SessionItem[])items.ToArray(typeof(SessionItem));
        }
    }
}